package com.mxpio.erp.common.order.service.impl;

import java.math.BigDecimal;
import java.util.*;

import com.mxpio.erp.common.enums.CommonEnums;
import com.mxpio.erp.common.enums.NoticeStatusEnums;
import com.mxpio.erp.common.inventory.entity.InvFlow;
import com.mxpio.erp.common.inventory.entity.SupplyChain;
import com.mxpio.erp.common.inventory.entity.WarehouseOrder;
import com.mxpio.erp.common.inventory.entity.WarehouseOrderLine;
import com.mxpio.erp.common.jpa.model.Notice;
import com.mxpio.erp.common.jpa.model.NoticeLine;
import com.mxpio.erp.common.quality.entity.InspectionBill;
import com.mxpio.erp.common.technology.entity.Item;
import com.mxpio.erp.common.technology.entity.ItemGroup;
import com.mxpio.erp.common.technology.entity.ItemQualityProp;
import com.mxpio.erp.common.vo.NoticeClassVo;
import com.mxpio.erp.common.vo.OrderExcuteLineVo;
import com.mxpioframework.common.exception.MBootException;
import com.mxpioframework.jpa.JpaUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums.BpmnStatus;
import com.mxpio.erp.common.enums.OrderStatusEnums.CloseStatus;
import com.mxpio.erp.common.enums.OrderStatusEnums.OrderStatus;
import com.mxpio.erp.common.jpa.model.Order;
import com.mxpio.erp.common.order.policy.OrderContext;
import com.mxpio.erp.common.order.policy.OrderPolicy;
import com.mxpio.erp.common.order.provider.OrderContextProvider;
import com.mxpio.erp.common.order.service.OrderService;
import com.mxpioframework.common.vo.Result;
import com.mxpioframework.security.util.SecurityUtils;
import com.mxpioframework.system.service.impl.BaseServiceImpl;

/**
 * 订单统一执行操作
 * @author mxpio
 *
 */
@Service
public class OrderServiceImpl extends BaseServiceImpl<Order> implements OrderService {
	
	@Autowired(required = false)
	private List<OrderContextProvider> orderContextProviders;


	/**
	 * 单据执行
	 */
	@Override
	@Transactional(readOnly = false)
	public OrderContext execute(Order order, OrderPolicy policy) {
		
		// 通用OrderContext初始化
		OrderContext context = OrderContext.init(order);
		policy.config(context);
		// 遍历匹配的 Context提供器 补充Context信息
		if(CollectionUtils.isNotEmpty(orderContextProviders)){
			for(OrderContextProvider provider : orderContextProviders){
				if(provider.supports(order.getClass())){
					provider.provide(context);
				}
			}
		}
		if(CollectionUtils.isEmpty(context.getErrors())){
			// 执行订单
			policy.apply(context);
		}
		
		return context;
	}
	
	@Override
	@Transactional(readOnly = false)
	public Result<Order> updateBpmnStatus(Order bizOrder, BpmnStatus bpmnStatus) {
		bizOrder.setBpmnStatus(bpmnStatus.getStatusCode());
		bizOrder.setReviewer(SecurityUtils.getLoginUsername());
		bizOrder.setReviewTime(new Date());
        this.update(bizOrder);
        return Result.OK();
	}

	@Override
	@Transactional(readOnly = false)
	public Result<Order> updateCloseStatus(Order bizOrder, CloseStatus closeStatus) {
		if(CloseStatus.CLOSED.equals(closeStatus) && bizOrder.getCloseStatus().equals(OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())){
			return Result.error("非打开状态不可关闭");
		}else if(CloseStatus.OPEN.equals(closeStatus) && bizOrder.getCloseStatus().equals(OrderStatusEnums.CloseStatus.OPEN.getStatusCode())){
			return Result.error("非关闭状态不可打开");
		}
		bizOrder.setCloseStatus(closeStatus.getStatusCode());
        this.update(bizOrder);
        return Result.OK(bizOrder);
	}

	@Override
	@Transactional(readOnly = false)
	public Result<? extends Order> updateOrderStatus(Order bizOrder, OrderStatus orderStatus) {
		bizOrder.setOrderStatus(orderStatus.getStatusCode());
        this.update(bizOrder);
        return Result.OK(bizOrder);
	}

	@Override
	@Transactional(readOnly = true)
	public void handleBizOrderQuantity(List<? extends Order> orders, List<NoticeClassVo> noticeClassVos, String accessType) {
		Map<String, BigDecimal> quantity = new HashMap<>();
		//Map<String, List<OrderLine>> map = orderLines.stream().collect(Collectors.groupingBy(OrderLine::getBizNo));
		Set<String> bizNos = new HashSet<>();
		orders.forEach(item ->bizNos.add(item.getBizNo()));

		//1.通过InvFlow计算实际执行数量
		List<InvFlow> flows = JpaUtil.linq(InvFlow.class).equal("originBizLineNo","0").in("originBizNo", bizNos).list();
		for(InvFlow flow : flows){

			//  若流水的AccessType为参数accessType 则为执行，否则为退货
			if(accessType.equals(flow.getAccessType())){
				BigDecimal actualQuantity = quantity.get("actual:"+flow.getOriginBizNo());
				if(actualQuantity == null){
					actualQuantity = BigDecimal.ZERO;
				}
				actualQuantity = actualQuantity.add(flow.getQuantity());
				quantity.put("actual:"+flow.getOriginBizNo(), actualQuantity);
			}

		}

		//2根据通知单和仓单计算计划数量
		//2.1计算通知单数量
		List<Notice> notices = new ArrayList<>();
		for(NoticeClassVo noticeClassVo : noticeClassVos){
			List<Notice> list = JpaUtil.linq(noticeClassVo.getNoticeClazz()).collect("noticeNo", noticeClassVo.getNoticeLineClazz(), "noticeNo")
					.equal("noticeStatus", NoticeStatusEnums.NoticeStatus.CREATE.getStatusCode()).in("bizOrderNo", bizNos)
					.list();
			notices.addAll(list);
		}
		for(Notice notice : notices){
			// 若通知单的AccessType为参数accessType 则为执行，否则为退货
			if(accessType.equals(notice.getAccessType())){
				for(NoticeLine line : notice.getNoticeLines()){
					BigDecimal planQuantity = quantity.get("plan:"+line.getOriginBizNo());
					if(planQuantity == null){
						planQuantity = BigDecimal.ZERO;
					}
					planQuantity = planQuantity.add(line.getQuantity());
					quantity.put("plan:"+line.getOriginBizNo(), planQuantity);
				}
			}
		}
		// 2.2 计算未完成仓单数量
		List<WarehouseOrder> wos = JpaUtil.linq(WarehouseOrder.class)
				.equal("closeStatus", OrderStatusEnums.CloseStatus.OPEN.getStatusCode())
				.notEqual("orderStatus", OrderStatusEnums.OrderStatus.FINISHED.getStatusCode())
				.in("originBizNo", bizNos).list();
		Map<String, WarehouseOrder> wosMap = JpaUtil.index(wos);

		List<InvFlow> invFlows = JpaUtil.linq(InvFlow.class).equal("originBizLineNo","0").in("woBizNo", wosMap.keySet()).list();
		Map<String, BigDecimal> flowMap = new HashMap<>();
		invFlows.forEach(flow -> {
			BigDecimal sumQuantity = flowMap.get(flow.getWoBizNo());
			if(sumQuantity == null){
				sumQuantity = BigDecimal.ZERO;
			}
			if(accessType.equals(flow.getAccessType())){
				sumQuantity = sumQuantity.add(flow.getQuantity());
			}else{
				sumQuantity = sumQuantity.subtract(flow.getQuantity());
			}
			flowMap.put(flow.getWoBizNo(), sumQuantity);
		});

		List<WarehouseOrderLine> woLines = JpaUtil.linq(WarehouseOrderLine.class)
				.equal("originBizLineNo","0")
				.in("bizNo", wosMap.keySet())
				.equal("closeStatus", OrderStatusEnums.CloseStatus.OPEN.getStatusCode()).list();
		for(WarehouseOrderLine woLine : woLines){
			if(accessType.equals(wosMap.get(woLine.getBizNo()).getAccessType())){
				BigDecimal planQuantity = quantity.get("plan:"+woLine.getOriginBizNo());
				BigDecimal sumQuantity = flowMap.get(woLine.getBizNo());
				if(planQuantity == null){
					planQuantity = BigDecimal.ZERO;
				}
				if(sumQuantity == null){
					sumQuantity = BigDecimal.ZERO;
				}
				planQuantity = planQuantity.add(woLine.getQuantity().subtract(sumQuantity));
				quantity.put("plan:"+woLine.getOriginBizNo(), planQuantity);
			}
		}
		//4.考虑生成的未检完成的质检单，归为计划部分
		List<InspectionBill> inspectionBills = JpaUtil.linq(InspectionBill.class)
				.notEqual("checkStatus", "3")//1计划，2检验中，3检验完成
				.in("originBizNo", bizNos).list();
		if (!inspectionBills.isEmpty()){
			for(InspectionBill line : inspectionBills){
				BigDecimal planQuantity = quantity.get("plan:"+line.getOriginBizNo());
				if(planQuantity == null){
					planQuantity = BigDecimal.ZERO;
				}
				planQuantity = planQuantity.add(line.getQuantity());
				quantity.put("plan:"+line.getOriginBizNo(), planQuantity);
			}
		}
		if (quantity.isEmpty()){
			orders.forEach(order -> {
				order.setActualQuantity(BigDecimal.ZERO);
				order.setPlanQuantity(BigDecimal.ZERO);
			});
		}else{
			for (Order order:orders){
				BigDecimal actualQuantity = quantity.containsKey("actual:"+order.getBizNo())&&quantity.get("actual:"+order.getBizNo())!=null?quantity.get("actual:"+order.getBizNo()):BigDecimal.ZERO;
				BigDecimal planQuantity = quantity.containsKey("plan:"+order.getBizNo())&&quantity.get("plan:"+order.getBizNo())!=null?quantity.get("plan:"+order.getBizNo()):BigDecimal.ZERO;
				order.setActualQuantity(actualQuantity);
				order.setPlanQuantity(planQuantity);
			}
		}
	}
	@Override
	@Transactional(readOnly = true)//关联是否入库检验信息 带供应链校验
	public void handleInQualityControl(String pnCode, List<OrderExcuteLineVo> lines) {
		this.handleInQualityControl(lines);
		Map<String,SupplyChain> supplyChainMap = new HashMap<>();
		for (OrderExcuteLineVo vo:lines){
			if (!StringUtils.equals(vo.getInQualityControl(), CommonEnums.YesNo.YES.getCode())){
				SupplyChain supplyChain = null;
				if (supplyChainMap.containsKey(vo.getItemCode())){
					supplyChain = supplyChainMap.get(vo.getItemCode());
				}else{
					supplyChain = JpaUtil.linq(SupplyChain.class)
							.equal("itemCode", vo.getItemCode())
							.equal("inQualityControl", CommonEnums.YesNo.YES.getCode())
							.equal("pnCode", pnCode).findOne();
					supplyChainMap.put(vo.getItemCode(), supplyChain);
				}
				if (supplyChain!=null){
					vo.setInQualityControl(CommonEnums.YesNo.YES.getCode());
				}
			}
		}
	}
	@Override
	@Transactional(readOnly = true)//关联是否入库检验信息
	public void handleInQualityControl(List<OrderExcuteLineVo> lines) {
		for (OrderExcuteLineVo vo:lines){
			vo.setInQualityControl(CommonEnums.YesNo.NO.getCode());//默认否
			//先查找物料组
			Item item = JpaUtil.linq(Item.class).equal("itemCode", vo.getItemCode()).findOne();
			if(item==null){
				throw new MBootException("物料编码"+vo.getItemCode()+"不存在");
			}
			ItemGroup groupCode = JpaUtil.linq(ItemGroup.class).equal("groupCode", item.getItemGroupCode()).findOne();
			if (groupCode!=null){
				ItemQualityProp itemGroupProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "group").equal("code", groupCode.getGroupCode()).findOne();
				if(itemGroupProp != null){
					if (CommonEnums.YesNo.YES.getCode().equals(itemGroupProp.getInQualityControl())){
						vo.setInQualityControl(itemGroupProp.getInQualityControl());
						continue;
					}
				}else{
					if (StringUtils.isNotEmpty(groupCode.getFaGroupCode())){
						ItemQualityProp parentGroupProp = this.getParentGroupProp(groupCode.getFaGroupCode());
						if(parentGroupProp != null && CommonEnums.YesNo.YES.getCode().equals(parentGroupProp.getInQualityControl())){
							vo.setInQualityControl(parentGroupProp.getInQualityControl());
							continue;
						}
					}
				}
			}
			//再查找物料
			ItemQualityProp itemProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "item").equal("code", vo.getItemCode()).findOne();
			if(itemProp != null && CommonEnums.YesNo.YES.getCode().equals(itemProp.getInQualityControl())){
				vo.setInQualityControl(itemProp.getInQualityControl());
			}
		}
	}

	private ItemQualityProp getParentGroupProp(String parentCode){//递归查找父级物料组对应的质量属性附表
		if (StringUtils.isBlank(parentCode)){
			return null;
		}
		ItemGroup groupCode = JpaUtil.linq(ItemGroup.class).equal("groupCode", parentCode).findOne();
		if (groupCode==null){
			return null;
		}
		ItemQualityProp itemGroupProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "group").equal("code", groupCode.getGroupCode()).findOne();
		if (itemGroupProp!=null){
			return itemGroupProp;
		}else{
			return getParentGroupProp(groupCode.getFaGroupCode());
		}
	}
}
